# !/usr/bin/env python
# -*-coding:utf-8 -*-
"""
# File       : base_response.py
# Time       ：2023/3/12 10:44
# Author     ：Y-aong
# version    ：python 3.7
# Description：
    自定义response
    custom response
"""
from flask_restx import fields, Namespace
from flask_restx.fields import MarshallingError

from public.logger import logger


class MarshalModel:
    def __init__(self, name: str, namespace: Namespace):
        self.base_db = {
            'id': fields.Integer(description='主键ID'),
            'insert_time': fields.String(description='插入时间'),
            'update_time': fields.String(description='修改时间'),
            'creator_name': fields.String(description='创建者'),
            'creator_id': fields.Integer(description='创建ID'),
            'updater_name': fields.String(description='修改者'),
            'updater_id': fields.Integer(description='创建ID'),
            'active': fields.Integer(description='是否软删除')
        }
        self.base_response = {
            'code': fields.Integer(readonly=True, default=200, description='http code'),
            'success': fields.Boolean(readonly=True, description='接口返回值状态'),
            'message': fields.String(readonly=True, description='接口返回数据'),
        }
        self.name = name
        self.namespace = namespace

    def option_response(self, nested_model):
        self.base_response['data'] = fields.Nested(nested_model, as_list=True)
        return self.namespace.model(f'{self.name}_opt_request', self.base_response)

    def detail_request(self, nested_model=None):
        if nested_model is None:
            return self.namespace.model(
                f'{self.name}_detail_request',
                {'id': fields.Integer(description='table id', required=True)}
            )
        else:
            return self.namespace.model(f'{self.name}_detail_request', nested_model)

    def table_id_response(self):
        table_id_item = {'table_id': fields.Integer(description='数据库id')}
        return self.namespace.model(f'{self.name}_table_id', table_id_item)

    def detail_response(self, nested_model: dict = None, is_db: bool = True):
        """详情信息响应"""
        if is_db and nested_model:
            nested_model.update(self.base_db)

        if nested_model:
            nested_model = self.namespace.model(f'{self.name}_db', nested_model)
            self.base_response['data'] = fields.Nested(nested_model)
        else:
            self.base_response['data'] = fields.String()

        return self.namespace.model(f'{self.name}_detail_response', self.base_response)

    def list_request(self, nested_model: dict = None, excludes: list = None, is_db: bool = True):
        if excludes is None:
            excludes = ['active']
        if nested_model and is_db and self.base_db:
            nested_model.update(self.base_db)

        temp = dict()
        for key, val in nested_model.items():
            if isinstance(excludes, list) and key in excludes:
                continue
            temp[key] = val
        return self.namespace.model(f'{self.name}_list_request', temp)

    def list_response(self, nested_model: dict, as_list: bool = True, is_db: bool = True):
        if is_db and nested_model:
            nested_model.update(self.base_db)

        if nested_model and is_db:
            db_model = self.namespace.model(f'{self.name}_db', nested_model)
            data = self.namespace.model(
                f'{self.name}_response_item',
                {
                    'list': fields.Nested(model=db_model, as_list=as_list),
                    'total': fields.Integer(),
                    'pageNum': fields.Integer(),
                    'pageSize': fields.Integer()
                }
            )
            self.base_response['data'] = fields.Nested(model=data)
        elif nested_model and not is_db:
            nested_model = self.namespace.model(f'{self.name}_db', nested_model)
            self.base_response['data'] = fields.Nested(model=nested_model, as_list=as_list)
        else:
            self.base_response['data'] = fields.String()
        return self.namespace.model(f'{self.name}_list_response', self.base_response)

    def create_request(self, nested_model: dict):
        if nested_model.get('id'):
            nested_model.pop('id')
        nested_model.update(self.base_db)
        return self.namespace.model(f'{self.name}_update_request', nested_model)

    def create_response(self, no_response=False):
        if no_response:
            self.base_response['data'] = fields.String(default=None)
        else:
            self.base_response['data'] = fields.Nested(self.table_id_response())
        self.base_response['data'] = fields.Nested(self.table_id_response())

        return self.namespace.model(f'{self.name}_create_response', self.base_response)

    def update_request(self, nested_model: dict):
        nested_model['id'] = fields.Integer(description='table id', required=True)
        nested_model.update(self.base_db)
        return self.namespace.model(f'{self.name}_update_request', nested_model)

    def update_response(self):
        self.base_response['data'] = fields.Nested(self.table_id_response())
        return self.namespace.model(f'{self.name}_update_response', self.base_response)

    def delete_request(self):
        return self.namespace.model(
            f'{self.name}_delete_request',
            {'id': fields.Integer(description='table id', required=True)}
        )

    def delete_response(self):
        self.base_response['data'] = fields.Nested(self.table_id_response())
        return self.namespace.model(f'{self.name}_delete_response', self.base_response)

    def message_response(self):
        self.base_response['data'] = fields.String(default='')
        return self.namespace.model(f'{self.name}_message_response', self.base_response)


def check_model_compliance(response_data, response_model):
    """递归地检查所提供的数据是否符合给定的模型。如果缺少或类型不匹配的字段 """
    model_fields = {key: value for key, value in response_model.items()}
    # Check for missing required fields
    missing_fields = [key for key, field in model_fields.items() if key not in response_data]
    if missing_fields:
        error_message = f'missing field {missing_fields}'
        logger.error(error_message)
        return dict(code=500, success=False, data=response_data, message=error_message)

    # Check for type mismatches
    for field_name, field_type in model_fields.items():
        if field_name in response_data:
            field_data = response_data[field_name]
            logger.debug(f'field_data::{field_data}， field_type::{field_type}')
            # Check Nested fields
            if isinstance(field_type, fields.Nested):
                nested_model = field_type.nested
                return check_model_compliance(field_data, nested_model)

            # Check other field types
            try:
                getattr(field_type, 'format')(field_data)
                return response_data
            except MarshallingError:
                error_message = f"field '{field_name}': expected {field_type}, got {type(field_data)}"
                logger.error(error_message)
                return dict(code=500, success=False, data=None, message=error_message)


def generate_response(data=None, code=200, message='success', model=None):
    """
    自定义响应, custom response
    :param code:状态码, status_code
    :param data:返回数据, response data
    :param message:返回消息, response_message
    :param model:返回模型, response_model
    :return:
    """
    success = True if code == 200 else False
    # 手动检查返回值是否符合定义的模型值
    response_data = dict(code=code, success=success, data=data, message=message)
    if model:
        return check_model_compliance(response_data, model)
    return response_data
